package com.tuandai.commons.alert;

import com.google.gson.Gson;
import com.tuandai.commons.encrypt.Md5Encryptor;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * AlertSender
 * Created by zhangzhenbin on 18-3-14.
 */
public class AlertSender {
    private static final String errorMsgPrefix = "发送告警失败：";
    private  ExecutorService executorService;
    private  ScheduledExecutorService scheduleExecutor = Executors.newScheduledThreadPool(1);
    private  AlertSenderConfig alertSenderConfig;
    private  Map<String, LinkedBlockingQueue<Date>> sendQueue = new HashMap<>();
    private  boolean inited;

    private void isInited() {
        if (!inited)
            throw new RuntimeException(errorMsgPrefix + "AlertSender未初始化！");
    }

    private void putSended(String serviceName) {
        if (!sendQueue.containsKey(serviceName)) {
            sendQueue.put(serviceName, new LinkedBlockingQueue<>());
        }
        sendQueue.get(serviceName).offer(new Date());
    }

    private void init(AlertSenderConfig alertSenderConfig){
        inited = true;
        this.alertSenderConfig = alertSenderConfig;
        executorService = Executors.newFixedThreadPool(alertSenderConfig.getConcurrentNum());
        //初始化自动过期
        scheduleExecutor.scheduleWithFixedDelay(() -> {
            Thread.currentThread().setName("自动过期线程");
            try {
                sendQueue.values().forEach(dates -> {
                    if (alertSenderConfig.getPolicy() == 1 || alertSenderConfig.getPolicy() == 2) {
                        while (dates.peek().toInstant().isBefore(LocalDateTime.now().plusHours(-1).atZone(ZoneId.systemDefault()).toInstant())) {
                            dates.poll();
                        }
                    } else {
                        while (dates.peek().toInstant().isBefore(LocalDateTime.now().plusMinutes(-1).atZone(ZoneId.systemDefault()).toInstant())) {
                            dates.poll();
                        }
                    }
                });
            } catch (Exception e) {
                //nothing to do!
            }
        }, 0, 10, TimeUnit.SECONDS);
    }

    public AlertSender(String platformHost) {
        AlertSenderConfig alertSenderConfig = new AlertSenderConfig();
        alertSenderConfig.setPlatformUrl(platformHost);
        init(alertSenderConfig);
    }

    /**
     * 初始化工具类
     * @param alertSenderConfig
     */
    public AlertSender(AlertSenderConfig alertSenderConfig) {
        init(alertSenderConfig);
    }

    /**
     * 发送Error级别的告警信息到告警平台
     * @param serviceName 业务名
     * @param msg 告警信息
     */
    public void sendError(String serviceName, String msg) {
        send(serviceName, "error", msg, null);
    }

    /**
     * 发送告警信息到告警平台
     * @param serviceName 业务名
     * @param level 告警级别
     * @param msg 告警信息
     * @param sendback 回调
     */
    public void send(String serviceName, String level, String msg, Sendback sendback) {
        //判断是否初始化工具类
        isInited();
        boolean sendPermission = true;
        if (alertSenderConfig.getPolicy() == 1 || alertSenderConfig.getPolicy() == 3) {
            int sum = sendQueue.values().stream().mapToInt(LinkedBlockingQueue::size).sum();
            if (sum >= alertSenderConfig.getPolicyNum()) {
                if (sendback != null) {
                    sendPermission = false;
                    sendback.apply(new HashMap<String, String>() {{
                        put("code", "500");
                        put("msg", errorMsgPrefix + "在1" + (alertSenderConfig.getPolicy() == 1 ? "小时" : "分钟") + "内发送告警超过[" + alertSenderConfig.getPolicyNum() + "]个！");
                    }});
                }
            }
        } else if (alertSenderConfig.getPolicy() == 2 || alertSenderConfig.getPolicy() == 4) {
            if (sendQueue.containsKey(serviceName) && sendQueue.get(serviceName).size() >= alertSenderConfig.getPolicyNum()) {
                if (sendback != null) {
                    sendPermission = false;
                    sendback.apply(new HashMap<String, String>() {{
                        put("code", "500");
                        put("msg", errorMsgPrefix + "serviceName为[" + serviceName + "]在1" + (alertSenderConfig.getPolicy() == 2 ? "小时" : "分钟") + "内发送告警超过[" + alertSenderConfig.getPolicyNum() + "]个！");
                    }});
                }
            }
        }
        if (sendPermission) {
            putSended(serviceName);
            executorService.submit(() -> {
                Map<String, String> res = sendAction(alertSenderConfig.getPlatformUrl(), alertSenderConfig.getAppName(), serviceName, level, msg);
                if (sendback != null)
                    sendback.apply(res);
            });
        }
    }

    /**
     * 发送异常动作！
     */
    private Map<String, String> sendAction(String platformHost, String appName, String serviceName, String level, String msg) {
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "获取IP地址失败！");
            }};
        }
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(platformHost);
        httpPost.setHeader("Accept", "application/json;charset=UTF-8");
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("appname", appName));
        nvps.add(new BasicNameValuePair("type", serviceName));
        nvps.add(new BasicNameValuePair("hostname", addr.getHostName()));
        nvps.add(new BasicNameValuePair("ip", addr.getHostAddress()));
        nvps.add(new BasicNameValuePair("level", level));
        nvps.add(new BasicNameValuePair("msg", msg));

        Date day = new Date();
        SimpleDateFormat df = new SimpleDateFormat("YYYYMMddHH");
        String currentTime = df.format(day);
        String token_2 = null; // 二次加密结果
        try {
            String token_1 = Md5Encryptor.md5Encode("MKiySs1YGlDvrHld"); // 一次加密结果
            token_2 = Md5Encryptor.md5Encode(token_1 + currentTime);
        } catch (Exception e) {
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "token加密异常！");
            }};
        }

        nvps.add(new BasicNameValuePair("token", token_2));
        try {
            httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "发送参数异常！");
            }};
        }

        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpPost);
        } catch (IOException e) {
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "提交请求异常！");
            }};
        }
        StringBuilder content = null;
        try {
            InputStream is = httpResponse.getEntity().getContent();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
            String tmp;
            content = new StringBuilder();
            while ((tmp = bufferedReader.readLine()) != null) content.append(tmp);
            is.close();
        } catch (IOException e) {
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "获取返回信息异常！");
            }};
        }

        String s = content.toString();
        Gson gson = new Gson();
        HashMap resMap = gson.fromJson(s, HashMap.class);
        try {
            int code = Integer.parseInt(resMap.get("code").toString());
            if (code != 200) {
                if (resMap.get("msg") != null) {
                    return new HashMap<String, String>() {{
                        put("code", resMap.get("code").toString());
                        put("msg", errorMsgPrefix + "告警平台返回错误信息：" + resMap.get("msg").toString());
                    }};
                } else {
                    return new HashMap<String, String>() {{
                        put("code", resMap.get("code").toString());
                        put("msg", errorMsgPrefix + "告警平台未返回错误信息!");
                    }};
                }
            }
        } catch (NumberFormatException e) {
            return new HashMap<String, String>() {{
                put("code", "500");
                put("msg", errorMsgPrefix + "告警平台未返回信息解析错误!");
            }};
        }
        if (resMap.get("msg") != null) {
            return new HashMap<String, String>() {{
                put("code", resMap.get("code").toString());
                put("msg", resMap.get("msg").toString());
            }};
        } else {
            return new HashMap<String, String>() {{
                put("code", resMap.get("code").toString());
                put("msg", "发送成功！");
            }};
        }
    }
}
